package com.czx.drivingtest.service.impl;

import cn.hutool.core.collection.CollUtil;
import com.czx.drivingtest.constant.RedisKeys;
import com.czx.drivingtest.dao.UserQuestionDao;
import com.czx.drivingtest.model.dto.ExamResult;
import com.czx.drivingtest.model.entity.Statistics;
import com.czx.drivingtest.model.entity.UserArchive;
import com.czx.drivingtest.model.entity.UserQuestion;
import com.czx.drivingtest.service.QuestionService;
import com.czx.drivingtest.service.UserQuestionService;
import com.czx.drivingtest.utils.IdWorker;
import com.czx.drivingtest.utils.JwtUtils;
import com.czx.drivingtest.utils.RedisUtils;
import com.czx.drivingtest.utils.ServletUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * @author czx
 * @version 2021/2/1 12:24 下午
 */
@Service
public class UserQuestionServiceImpl implements UserQuestionService {
    @Autowired
    private RedisUtils redisUtils;
    @Autowired
    private UserQuestionDao userQuestionDao;
    @Autowired
    private QuestionService questionService;


    @Override
    public UserArchive getUserArchive(long userId, Integer subject) {
        String key = String.format(RedisKeys.USER_ARCHIVE, userId, subject);
        UserArchive userArchive = (UserArchive) redisUtils.get(key);
        if (userArchive != null) {
            int totalCount = questionService.getTotalCount(subject);
            userArchive.setTotalCount(totalCount);
            return userArchive;
        }
        userArchive = userQuestionDao.queryUserArchive(userId, subject);
        if (userArchive != null) {
            userArchive.setSubject(subject);
            Integer lastQuestionId = userQuestionDao.queryLastQuestionId(userId, subject);
            userArchive.setLastQuestionId(lastQuestionId);
            int totalCount = questionService.getTotalCount(subject);
            userArchive.setTotalCount(totalCount);
            redisUtils.setExpire(key, userArchive, 1, TimeUnit.DAYS);
        }
        return userArchive;
    }

    @Override
    public void updateUserArchive(UserArchive userArchive) {
        userQuestionDao.updateUserArchive(userArchive);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void completeQuestion(long userId, Integer status, Integer questionId, boolean wasInOrder) {
        Integer subject = questionService.getSubjectByQuestionId(questionId);
        //查出用户的存档信息
        UserArchive userArchive = getUserArchive(userId, subject);
        UserQuestion userQuestion = userQuestionDao.query(userId, questionId);
        //先判断这道题用户以前是否答过
        if (userQuestion == null) {
            userQuestion = new UserQuestion(questionId, userId, status);
            userQuestionDao.insert(userQuestion);
            userArchive.increaseCompleteCount();
            if (status == 1) {  //如果答错了, 错题总数 +1
                userArchive.increaseFalseCount();
            }
        } else {
            if (userQuestion.getStatus() == 2) {
                //如果原本是对的, 现在又做错了, 那么将错题数 +1
                if (status == 1) {
                    userArchive.increaseFalseCount();
                }
            } else {
                //如果原本是错的,现在做对了, 那么将错题数 -1
                if (status == 2) {
                    userArchive.decreaseFalseCount();
                }
            }
            userQuestion.setStatus(status);
            userQuestionDao.updateStatus(userQuestion);
        }

        userArchive.setUserId(userId);
        //如果是顺序练习, 就将用户的最后一题更新
        if (wasInOrder) {
            userArchive.setLastQuestionId(questionId);
            updateUserArchive(userArchive);
        }
        String key = String.format(RedisKeys.USER_ARCHIVE, userId, subject);
        redisUtils.setExpire(key, userArchive, 1, TimeUnit.DAYS);   //更新缓存要放在更新数据库之后,因为缓存不会事务回滚
    }

    @Override
    public List<UserQuestion> listUserQuestions(long userId, Integer subject) {
        //获取所有题目的id
        List<Integer> questionIdList = questionService.getQuestionIdList(subject);
        Map<Integer, Integer> questionStatusMap = questionStatusMap(userId, subject);
        List<UserQuestion> result = new ArrayList<>(questionIdList.size());
        for (Integer questionId : questionIdList) {
            Integer status = questionStatusMap.get(questionId);
            if (status != null) {
                result.add(new UserQuestion(questionId, userId, status));
            } else {
                result.add(new UserQuestion(questionId, userId, 0));
            }
        }
        return result;
    }

    @Override
    public List<UserQuestion> getWrongQuestions(long userId, Integer subject) {
        List<UserQuestion> userQuestions = userQuestionDao.queryWrongByUserId(userId, subject);
        return userQuestions;
    }

    @Override
    public void handPaper(int trueCount, int subject) {
        String authorization = ServletUtils.getRequestHeader("Authorization");
        long userId = JwtUtils.analysisUserId(authorization);
        ExamResult examResult = new ExamResult();
        examResult.setId(IdWorker.nextId());
        examResult.setUserId(userId);
        examResult.setScore(trueCount);
        examResult.setSubject(subject);
        examResult.setCreateTime(new Date());
        userQuestionDao.insertExamResult(examResult);
    }

    @Override
    public Statistics getStatistics(long userId, int subject) {
        //获取用户的累计考试和及格次数
        Statistics result = userQuestionDao.queryStatistics(userId, subject);
        //获取用户的累计答题数
        int answerCount = userQuestionDao.queryAnswerCount(userId, subject);
        result.setAnswerCount(answerCount);
        return result;
    }

    /**
     * 获取用户答题的map
     * key为题目id, value为这道题回答情况
     *
     * @param userId
     * @param subject
     * @return
     */
    private Map<Integer, Integer> questionStatusMap(long userId, Integer subject) {
        List<UserQuestion> userQuestions = userQuestionDao.queryByUserId(userId, subject);
        if (CollUtil.isEmpty(userQuestions)) {
            return new HashMap<>(0);
        }
        return userQuestions.stream()
                .collect(Collectors.toMap(UserQuestion::getQuestionId, UserQuestion::getStatus));
    }
}
